package cn.sz.dzy.rest.controller;

import cn.sz.dzy.rest.dto.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import java.io.*;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program: RestTemplate
 * @description:
 * @author: DingZhenYun
 * @create: 2024-04-18 15:51
 **/
@RestController
@RequestMapping("/down")
@Slf4j
public class DownstreamController {
    @Resource
    private RestTemplate restTemplate;

    private static final String SERVICE_PROVIDER = "http://localhost:4000";

    @GetMapping("/get1")
    public UserDto get1() {
        return restTemplate.getForObject(SERVICE_PROVIDER + "/up/get", UserDto.class);
    }

    @GetMapping("/get2")
    public UserDto get2() {
        //getForEntity方法，返回值为ResponseEntity类型
        // ResponseEntity中包含了响应结果中的所有信息，比如头、状态、body
        ResponseEntity<UserDto> responseEntity = restTemplate.getForEntity(SERVICE_PROVIDER + "/up/get", UserDto.class);
        //状态码
        log.info("状态码:{}", responseEntity.getStatusCode());
        log.info("头:{}", responseEntity.getHeaders());
        //获取body
        UserDto dto = responseEntity.getBody();
        System.out.println(dto);
        return dto;
    }


    @GetMapping("/get3")
    public UserDto get3() {
        //url中有动态参数
        Map uriVariables = new HashMap<>();
        uriVariables.put("id", "1");
        uriVariables.put("name", "ikun");
        //使用getForObject或者getForEntity方法
        UserDto userDto1 = restTemplate.getForObject(SERVICE_PROVIDER + "/up/get/{id}/{name}", UserDto.class, uriVariables);
        System.out.println(userDto1);
        ResponseEntity responseEntity = restTemplate.getForEntity(SERVICE_PROVIDER + "/up/get/{id}/{name}", UserDto.class, uriVariables);
        UserDto userDto2 = (UserDto) responseEntity.getBody();
        System.out.println(userDto2);

        URI uri1 = UriComponentsBuilder.fromPath("/up/get/{id}/{name}")
                .scheme("http")
                .host("localhost")
                .port(4000)
                .buildAndExpand(123, "john").toUri();
        ResponseEntity responseEntity1 = restTemplate.getForEntity(uri1, UserDto.class);
        UserDto userDto3 = (UserDto) responseEntity1.getBody();
        System.out.println(userDto3);


        UserDto userDto4 = restTemplate.getForObject(SERVICE_PROVIDER + "/up/get/{id}/{name}", UserDto.class, 1, "张三");
        System.out.println(userDto4);
        return userDto3;
    }

    @GetMapping("/get4")
    public void get4() {
        //若返回结果是泛型类型的，需要使用到exchange方法，
        //这个方法中有个参数是ParameterizedTypeReference类型，通过这个参数类指定泛型类型
        ResponseEntity<List<UserDto>> responseEntity =
                restTemplate.exchange(SERVICE_PROVIDER + "/up/getList",
                        HttpMethod.GET,
                        null,
                        new ParameterizedTypeReference<List<UserDto>>() {
                        });
        List list = responseEntity.getBody();
        System.out.println(list);
    }

    @GetMapping("/get5")
    public void get5() {
        Map uriVariables = new HashMap<>();
        uriVariables.put("id", "1");
        uriVariables.put("name", "ikun");
        //使用getForObject或者getForEntity方法
        UserDto userDto1 = restTemplate.getForObject(SERVICE_PROVIDER + "/up/geByParam?id={1}&name={2}", UserDto.class, 1, "aaa");
        System.out.println(userDto1);
        UserDto userDto2 = restTemplate.getForObject(SERVICE_PROVIDER + "/up/geByParam?id={id}&name={name}", UserDto.class, uriVariables);
        System.out.println(userDto2);
        ResponseEntity useDto3 = restTemplate.getForEntity(SERVICE_PROVIDER + "/up/geByParam?id={id}&name={name}", UserDto.class, uriVariables);
        System.out.println((UserDto) useDto3.getBody());
        ResponseEntity<UserDto> useDto4 = restTemplate.getForEntity(SERVICE_PROVIDER + "/up/geByParam?id={1}&name={2}", UserDto.class, 1, "aaa");
        System.out.println((UserDto) useDto4.getBody());

        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(SERVICE_PROVIDER + "/up/geByParam");
        builder.queryParam("id", 1);
        builder.queryParam("name", "11");
        URI uri = builder.build().toUri();
        ResponseEntity<UserDto> useDto5 = restTemplate.getForEntity(uri, UserDto.class);
        System.out.println((UserDto) useDto5.getBody());


        URI uri1 = UriComponentsBuilder.fromUriString(SERVICE_PROVIDER + "/up/geByParam")
                .queryParam("id", 1)
                .queryParam("name", 1).build().toUri();
        ResponseEntity<UserDto> useDto6 = restTemplate.getForEntity(uri1, UserDto.class);
        System.out.println((UserDto) useDto6.getBody());

    }

    @GetMapping("/get6")
    public void get6() {
        //文件比较小的情况，直接返回字节数组
        ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(SERVICE_PROVIDER+"/up/downFile", byte[].class);
        //获取文件的内容
        byte[] body = responseEntity.getBody();
        String content = new String(body);
        System.out.println(content);
    }


    @GetMapping("/get7")
    public void get7() {
        /**
         * 文件比较大的时候，比如好几个G，就不能返回字节数组了，会把内存撑爆，导致OOM
         * 需要这么玩：
         * 需要使用execute方法了，这个方法中有个ResponseExtractor类型的参数，
         * restTemplate拿到结果之后，会回调{@link ResponseExtractor#extractData}这个方法，
         * 在这个方法中可以拿到响应流，然后进行处理，这个过程就是变读边处理，不会导致内存溢出
         */
        String result = restTemplate.execute(SERVICE_PROVIDER+"/up/downBigFile",
                HttpMethod.GET,
                null,
                new ResponseExtractor() {
                    @Override
                    public String extractData(ClientHttpResponse response) throws IOException {
                        System.out.println("状态："+response.getStatusCode());
                        System.out.println("头："+response.getHeaders());
                        //获取响应体流
                        InputStream body = response.getBody();
                        //处理响应体流
                        String content = IOUtils.toString(body, "UTF-8");
                        return content;
                    }
                }, new HashMap<>()).toString();

        System.out.println(result);
    }


    @GetMapping("/get8")
    public void get8() {
        String url = SERVICE_PROVIDER+"/up/header";
        //①：请求头放在HttpHeaders对象中
        MultiValueMap headers = new HttpHeaders();
        headers.add("header-1", "V1");
        headers.add("header-2", "Spring");
        headers.add("header-2", "SpringBoot");
        HttpEntity<Object> entity = new HttpEntity<>(null, headers);
        ResponseEntity<Object> exchange = restTemplate.exchange(url, HttpMethod.GET, entity, Object.class);
        System.out.println(exchange.getBody());
    }

    @GetMapping("/get9")
    public void get9() {
        String url = SERVICE_PROVIDER+"/up/getAll/{path1}/{path2}";
        //①：请求头
        MultiValueMap headers = new HttpHeaders();
        headers.add("header-1", "V1");
        headers.add("header-2", "Spring");
        headers.add("header-2", "SpringBoot");
        //②：url中的2个参数
        Map uriVariables = new HashMap<>();
        uriVariables.put("path1", "v1");
        uriVariables.put("path2", "v2");

        //③：HttpEntity：HTTP实体，内部包含了请求头和请求体
        HttpEntity requestEntity = new HttpEntity(
                null,//body部分，get请求没有body，所以为null
                headers //头
        );
        //④：使用exchange发送请求
        ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<Map>() {
                },
                uriVariables);

        System.out.println(exchange.getBody());
    }


    @GetMapping("/get10")
    public void get10() {
        //①：表单信息，需要放在MultiValueMap中，MultiValueMap相当于Map>
        MultiValueMap body = new LinkedMultiValueMap<>();
        //调用add方法填充表单数据(表单名称:值)
        body.add("id","1");
        body.add("name","SpringMVC系列");
        body.add("age","10");
        //②：发送请求(url,请求体，返回值需要转换的类型)
        UserDto result = restTemplate.postForObject(SERVICE_PROVIDER+"/up/form", body, UserDto.class);
        System.out.println(result);
    }

    @GetMapping("/get11")
    public void get11() {
        String url = SERVICE_PROVIDER+"/up/form2";
        //①：表单信息，需要放在MultiValueMap中，MultiValueMap相当于Map>
        MultiValueMap body = new LinkedMultiValueMap<>();
        //调用add方法放入表单元素(表单名称:值)
        //②：文件对应的类型，需要是org.springframework.core.io.Resource类型的，常见的有[FileSystemResource、InputStreamResource、ByteArrayResource]
        body.add("file", new FileSystemResource("G:\\es\\RestTemplate\\src\\main\\java\\cn\\sz\\dzy\\rest\\dto\\UserDto.java"));
        //③：头
        HttpHeaders headers = new HttpHeaders();
        headers.add("header1", "v1");
        headers.add("header2", "v2");
        //④：请求实体
        RequestEntity requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
        //⑤：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<Map> exchange = restTemplate.exchange(
                requestEntity,
                new ParameterizedTypeReference<Map>() {
                });
        Map result = exchange.getBody();
        System.out.println(result);

        HttpEntity httpEntity = new HttpEntity(
                body,//body部分，get请求没有body，所以为null
                headers //头
        );
        ResponseEntity<Map> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        Map result1 = exchange1.getBody();
        System.out.println(result1);
    }

    @GetMapping("/get12")
    public void get12() {
        String url = SERVICE_PROVIDER+"/up/form2";
        //①：表单信息，需要放在MultiValueMap中，MultiValueMap相当于Map>
        MultiValueMap body = new LinkedMultiValueMap<>();
        /**
         * ②：通过流的方式上传文件，流的方式需要用到InputStreamResource类，需要重写2个方法
         * getFilename：文件名称
         * contentLength：长度
         */
        InputStream inputStream= null;
        try {
            inputStream = new FileInputStream(new File("C:\\Users\\dingz\\Desktop\\file\\1.txt"));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        InputStream finalInputStream = inputStream;
        InputStreamResource inputStreamResource = new InputStreamResource(finalInputStream) {
            @Override
            public String getFilename() {
                return "1.txt";
            }

            @Override
            public long contentLength() throws IOException {
//                return finalInputStream.available();
                return 0L;
            }
        };
        body.add("file", inputStreamResource);
        //③：头
        HttpHeaders headers = new HttpHeaders();
        headers.add("header1", "v1");
        headers.add("header2", "v2");
        //④：请求实体
        RequestEntity<MultiValueMap> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
        //⑤：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<Map> exchange = restTemplate.exchange(
                requestEntity,
                new ParameterizedTypeReference<Map>() {
                });
        Map result = exchange.getBody();
        System.out.println(result);

        System.out.println("方式二:");
        HttpEntity httpEntity = new HttpEntity(
                body,//body部分，get请求没有body，所以为null
                headers //头
        );
        ResponseEntity<Map> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        Map result1 = exchange1.getBody();
        System.out.println(result1);
    }



    @GetMapping("/get13")
    public void get13() {
        String url = SERVICE_PROVIDER+"/up/form3";
        //①：表单信息，需要放在MultiValueMap中，MultiValueMap相当于Map>
        MultiValueMap body = new LinkedMultiValueMap<>();
        body.add("name", "11");
        body.add("headImg", new FileSystemResource(".\\src\\main\\resources\\1.jpg"));
        //来2张证件照，元素名称一样"路人"
        body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\2.jpg"));
        body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\3.jpg"));
        //③：头
        HttpHeaders headers = new HttpHeaders();
        headers.add("header1", "v1");
        headers.add("header2", "v2");
        //④：请求实体
        RequestEntity<Map> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
        //⑤：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<Map> exchange = restTemplate.exchange(
                requestEntity,
                new ParameterizedTypeReference<Map>() {
                });
        Map result = exchange.getBody();
        System.out.println(result);


        System.out.println("方式二:");
        HttpEntity httpEntity = new HttpEntity(
                body,//body部分
                headers //头
        );
        ResponseEntity<Map> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        Map result1 = exchange1.getBody();
        System.out.println(result1);

    }


    @GetMapping("/get14")
    public void get14() {
        String url = SERVICE_PROVIDER+"/up/form4";
        UserDto userDto=new UserDto(1,"iku",123);
        System.out.println("方法一:");
        UserDto result = restTemplate.postForObject(url, userDto, UserDto.class);
        System.out.println(result);

        System.out.println("方法二:");
        UserDto userDto1 = restTemplate.postForObject(URI.create(url), userDto, UserDto.class);
        System.out.println(userDto1);

        System.out.println("方法三:");
        ResponseEntity<UserDto> userDtoResponseEntity = restTemplate.postForEntity(url, userDto, UserDto.class);
        UserDto body = userDtoResponseEntity.getBody();
        System.out.println(body);
        System.out.println("方法四:");
        ResponseEntity<UserDto> userDtoResponseEntity1 = restTemplate.postForEntity(URI.create(url), userDto, UserDto.class);
        UserDto body1 = userDtoResponseEntity1.getBody();
        System.out.println(body1);
        System.out.println("方法五:");

        HttpEntity httpEntity = new HttpEntity(
                body,//body部分
                null //头
        );
        ResponseEntity<Map> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        Map result1 = exchange1.getBody();
        System.out.println(result1);

        System.out.println("方法六:");
        RequestEntity<UserDto> requestEntity = new RequestEntity<>(body, null, HttpMethod.POST, URI.create(url));
        //⑤：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<UserDto> exchange = restTemplate.exchange(requestEntity,UserDto.class);
        UserDto body2 = exchange.getBody();
        System.out.println(body2);
    }






    @GetMapping("/get15")
    public void get15() {
        String url = SERVICE_PROVIDER+"/up/form5";
        //①：请求体，发送的时候会被转换为json格式数据
        List body = Arrays.asList(
                new UserDto(1, "IKUN",1),
                new UserDto(2, "KUNKUN",2));
        //②：头
        HttpHeaders headers = new HttpHeaders();
        headers.add("header1", "v1");
        headers.add("header2", "v2");

        HttpEntity httpEntity = new HttpEntity(
                body,//body部分
                headers //头
        );
        ResponseEntity<List> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, List.class);
        List body1 = exchange1.getBody();
        System.out.println(body1);
        System.out.println("方法2:");

        //③：请求实体
        RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
        //④：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<List> exchange = restTemplate.exchange(
                requestEntity,
                List.class);
        //⑤：获取结果
        List result = exchange.getBody();
        System.out.println(result);
    }

    @GetMapping("/get16")
    public void get16() {
        String url = SERVICE_PROVIDER+"/up/form5";
        //①：请求体为一个json格式的字符串
        String body = "[{\"id\":1,\"name\":\"IKUN\"},{\"id\":2,\"name\":\"KUNKUN\"}]";
        /**
         * ②：若请求体为json字符串的时候，需要在头中设置Content-Type=application/json；
         * 若body是普通的java类的时候，无需指定这个，RestTemplate默认自动配上Content-Type=application/json
         */
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        //③：请求实体
        RequestEntity requestEntity = new RequestEntity(body, headers, HttpMethod.POST, URI.create(url));
        //④：发送请求(请求实体，返回值需要转换的类型)
        ResponseEntity<List> exchange = restTemplate.exchange(
                requestEntity,
                List.class);
        //⑤：获取结果
        List result = exchange.getBody();
        System.out.println(result);

        System.out.println("方法2:");

        HttpEntity httpEntity = new HttpEntity(
                body,//body部分
                headers //头
        );
        ResponseEntity<List> exchange1 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, List.class);
        List body2 = exchange1.getBody();
        System.out.println(body2);
    }
}
